/*
 * Copyright (C) 2011 Michael Widmer <widmm8@bfh.ch>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package ch.bfh.aeneas.provider.storage.db;

import android.content.ContentValues;
import ch.bfh.aeneas.exchange.data.IdRoute;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.location.Location;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
import ch.bfh.aeneas.exchange.data.Categories;
import ch.bfh.aeneas.exchange.data.Node;
import ch.bfh.aeneas.exchange.data.Node.NodeType;
import ch.bfh.aeneas.exchange.storage.ParcelableTrackStorage;
import ch.bfh.aeneas.exchange.data.Route;
import ch.bfh.aeneas.exchange.data.NoItemFoundException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

/**
 *
 * @author Michael Widmer <widmm8@bfh.ch>
 */

public class Database implements ParcelableTrackStorage {

    private static Database mInstance;
    
    private SQLiteDatabase mDb;
    
    private static final String LOG_TAG = "Database";
    
    protected static final String COLUMN_NAME = "name";
    protected static final String COLUMN_DATE = "date";
    protected static final String COLUMN_GROUP = "collection";
    protected static final String COLUMN_ORDER = "order";

    public static final Parcelable.Creator<Database> CREATOR
        = new Parcelable.Creator<Database>() {
        public Database createFromParcel(Parcel in) {
            return Database.getInstance(in);
        }

        public Database[] newArray(int size) {
            return new Database[1];
        }
    };
    
    private Database(SQLiteOpenHelper soh) {
        mDb = soh.getWritableDatabase();
    }

    public static Database getInstance(Parcel in) {
        Log.i(LOG_TAG, "Request for instance");
        if(mInstance != null)
            return mInstance;
        else
            return null;
    }
    
    public static Database getInstance(SQLiteOpenHelper soh) {
        Log.i(LOG_TAG, "Request for instance");
        if(mInstance == null)
            mInstance = new Database(soh);
        return mInstance;
    }
    
    public Route addRoute(String name) {
        
        Route route = null;
        
        mDb.beginTransaction();
        try {
            ContentValues cv = new ContentValues();
            cv.put("name",name);
            
            long id = mDb.insertOrThrow(DBOpenHelper.TABLE_ROUTES, "address", cv);
            Log.i(LOG_TAG, "Received id "+id+" for last inserted route");
            route = new DbRoute(id, name, new Date(),false);
            
            mDb.setTransactionSuccessful();
        } catch(SQLException ex) {
            Log.i(LOG_TAG, "Error while adding route: "+ex.getMessage());
        } finally {
            mDb.endTransaction();
            
            Cursor c = mDb.rawQuery("SELECT COUNT(*) FROM routes",null);
            Log.i(LOG_TAG, "Containing "+c.getCount()+" rows/routes!");
            
            return route;
        }        
    }

    public Route getRouteById(long id) throws NoItemFoundException {
        String argument = Long.toString(id);
        Cursor rCursor = mDb.query(DBOpenHelper.TABLE_ROUTES, null, "`route_id` = "+argument, null, null, null, null);
        
        if(rCursor == null) {
            throw new NoItemFoundException("IRoute",argument);
        }
        rCursor.moveToFirst();
        IdRoute r = parseRoute(rCursor);
        Log.i(LOG_TAG, "Found "+rCursor.getCount()+" routes for id "+argument);
        Log.i(LOG_TAG, "Resulted route: "+r);
        r.setNodes(getNodesByRoute(r));
        
        rCursor.close();        
        return r;
    }

    public Route getRouteByName(String name) throws NoItemFoundException {
        Cursor rCursor = mDb.query(DBOpenHelper.TABLE_ROUTES, null, "`name` LIKE "+name, null, null, null, null);
        
        if(rCursor == null) {
            throw new NoItemFoundException("IRoute",name);
        }
        
        rCursor.moveToFirst();
        DbRoute r = parseRoute(rCursor);
        r.setNodes(getNodesByRoute(r));
        
        rCursor.close();        
        return r;
    }

    public List<Route> getRoutes() throws NoItemFoundException {
        //Cursor rCursor = mDb.query(DBOpenHelper.TABLE_ROUTES, new String[]{"name","date"}, "`collection` <> 0", null, null, null, null);
        Cursor rCursor = mDb.query(DBOpenHelper.TABLE_ROUTES, new String[]{"route_id", "name","date"}, null, null, null, null, null);
        
        if(rCursor == null || rCursor.getCount() < 1) {
            throw new NoItemFoundException("IRoute");
        }
        Log.i(LOG_TAG, "Received cursor with count "+rCursor.getCount());
        
        List<Route> routes = new ArrayList<Route>();
        
        DbRoute r;
        
        rCursor.moveToFirst();
        while(!rCursor.isAfterLast()) {
            r = parseRoute(rCursor);
            r.setNodes(getNodesByRoute(r));
            routes.add(r);
            rCursor.moveToNext();
        }
        
        rCursor.close();
        
        Log.i(LOG_TAG, "Found "+routes.size()+" routes!");
        return routes;
    }
    
    public List<Route> getRoutesByNode(Node node) throws NoItemFoundException {
        DbNode dbn = (DbNode) resolveNode(node);
        Cursor rCursor = mDb.query(DBOpenHelper.TABLE_ROUTES_NODES, null, "`node_id` = "+dbn.getId(), null, "`route_id`", null, null);
        
        if(rCursor == null || rCursor.getCount() < 1) {
            throw new NoItemFoundException("IRoute");
        }
        
        List<Route> routes = new ArrayList<Route>();
        
        DbRoute r;
        
        rCursor.moveToFirst();
        while(!rCursor.isAfterLast()) {
            r = parseRoute(rCursor);
            r.setNodes(getNodesByRoute(r));
            routes.add(r);
            rCursor.moveToNext();
        }
        
        rCursor.close();        
        return routes;
    }

    public List<Route> getRoutesNear(Location loc, int distance) throws NoItemFoundException {
        return this.getRoutesNear(loc.getLatitude(), loc.getLongitude(), distance);
    }

    public List<Route> getRoutesNear(double latitude, double longitude, int distance) throws NoItemFoundException {
        Route r;
        List<Route> routes = new ArrayList<Route>();
        
        String[] arguments = new String[4];

        double latRadian = Math.toRadians(latitude);

        double degLatKm = 110.574235;
        double degLongKm = 110.572833 * Math.cos(latRadian);
        double deltaLat = distance / 1000.0 / degLatKm;
        double deltaLong = distance / 1000.0 / degLongKm;

        double minLat = latitude - deltaLat;
        double minLong = longitude - deltaLong;
        double maxLat = latitude + deltaLat;
        double maxLong = longitude + deltaLong;

        arguments[0] = Double.toString(minLat);
        arguments[1] = Double.toString(maxLat);
        arguments[2] = Double.toString(minLong);
        arguments[3] = Double.toString(maxLong);

        String sql = "SELECT DISTINCT t1.`route_id`, t1.`name`, t1.`date` FROM "+DBOpenHelper.TABLE_ROUTES+" t1 " +
                     "LEFT JOIN "+DBOpenHelper.TABLE_ROUTES_NODES+" t2 ON t1.`route_id` = t2.`route_id` " +
                     "LEFT JOIN "+DBOpenHelper.TABLE_NODES+" t3 on t2.`node_id` = t3.`node_id` " +
                     "WHERE t3.`latitude` > ? AND t3.`latitude` < ? " +
                     "AND t3.`longitude` > ? AND t3.`longitude` < ? ";
        
        Cursor rCursor = mDb.rawQuery(sql, arguments);
        //List<INode> nodes = new ArrayList<INode>();
        if(rCursor != null && rCursor.getCount() > 0)
        {
            rCursor.moveToFirst();
            int id;
            int id_index = rCursor.getColumnIndex("route_id");
            while(!rCursor.isAfterLast())
            {
                id = rCursor.getInt(id_index);
                
                r = parseRoute(rCursor);
                r.addNodes(getNodesByRoute(r));
                routes.add(r);
                
                rCursor.moveToNext();
            }
        }//*/
        return routes;
    }

    public Node addPOI(String name, Location loc) {
        return this.addNode(name, NodeType.POI, loc.getLatitude(), loc.getLongitude(), loc.getAltitude(), loc.getAccuracy(), false);
    }
    
    public Node addPOI(String name, double latitude, double longitude, double altitude, float accuracy) {
        return this.addNode(name, NodeType.POI, latitude, longitude, altitude, accuracy, false);
    }

    public Node addNode(String name, Location loc) {
        return this.addNode(name, NodeType.WAYPOINT, loc.getLatitude(), loc.getLongitude(), loc.getAltitude(), loc.getAccuracy(), true);
    }
    
    public Node addNode(String name, double latitude, double longitude, double altitude, float accuracy) {
        return this.addNode(name, NodeType.WAYPOINT, latitude, longitude, altitude, accuracy, true);
    }
    
    private Node addNode(String name, NodeType type, double latitude, double longitude, double altitude, float accuracy, boolean generated) {
        
        Node node = null;
        
        mDb.beginTransaction();
        try {
            ContentValues cv = new ContentValues();
            cv.put("name",name);
            cv.put("type",type.toString());
            cv.put("latitude",latitude);
            cv.put("longitude",longitude);
            cv.put("altitude",altitude);
            cv.put("accuracy",accuracy);
            cv.put("generated", generated);
            
            long id = mDb.insertOrThrow(DBOpenHelper.TABLE_NODES, "address", cv);
            
            Log.i(LOG_TAG, "Received id "+id+" for last inserted node");
            
            node = new DbNode(id, name, type, new Date(), latitude, longitude, altitude, accuracy,"",generated);
            
            mDb.setTransactionSuccessful();
        } catch(SQLException ex) {
            Log.i(LOG_TAG, "Error while adding node: "+ex.getMessage());
        } finally {
            mDb.endTransaction();
            
            Cursor c = mDb.rawQuery("SELECT COUNT(*) FROM nodes", null);
            Log.i(LOG_TAG, "Containing "+c.getCount()+" rows/nodes!");
            return node;
        }
    }

    public boolean convertToPOI(Node node) {
        boolean successful = false;
        
        DbNode dn;
        String id;
        
        if(!(node instanceof DbNode)) {
            return successful;
        } else {
            dn = (DbNode)node;
            id = Long.toString(dn.getId());
        }
        
        mDb.beginTransaction();
        try {
            
            ContentValues cv = new ContentValues();
            cv.put("type",NodeType.POI.toString());
            
            mDb.updateWithOnConflict(DBOpenHelper.TABLE_NODES, cv, "WHERE `node_id` = ?", new String[] {id}, SQLiteDatabase.CONFLICT_ROLLBACK);
            mDb.setTransactionSuccessful();
            
            successful = true;
        } catch(SQLException ex) {
            
        } finally {
            mDb.endTransaction();
            return successful;
        } 
    }

    public boolean addRouteNode(Route route, Node node) {
        boolean successful = false;
        DbRoute dr;
        DbNode dn;
        
        if(!(route instanceof DbRoute)) {
            return successful;
        } else {
            dr = (DbRoute) route;
        }
        
        if(!(node instanceof DbNode)) {
            return successful;
        } else {
            dn = (DbNode) node;
        }
        
        mDb.beginTransaction();
        try {
            ContentValues cv = new ContentValues();
            cv.put("route_id",dr.getId());
            cv.put("node_id",dn.getId());
            
            mDb.insertOrThrow(DBOpenHelper.TABLE_ROUTES_NODES, null, cv);            
            mDb.setTransactionSuccessful();
            
            successful = true;
        } catch(SQLException ex) {
            
        } finally {
            mDb.endTransaction();
            return successful;
        }
        
    }

    public List<Node> findNodesNear(double latitude, double longitude, int distance) {
        Node n;
        List <Node> nodes = new ArrayList<Node>();
        
        /*n = new IdNode(66L, "Bi dire Mer",NodeType.POI,new Date(2011, 11, 28, 22, 42),88.8888888,7.7777777,563.0,10,"Kirschenackerweg 8",false);
        nodes.add(n);*/
        
        /*distance = distance/1000;
        GeoLocation location = GeoLocation.fromDegrees(latitude, longitude);
        GeoLocation[] bounds = location.boundingCoordinates(distance,GeoLocation.EARTH_RADIUS);
        boolean meridian180WithinDistance = 
                        bounds[0].getLongitudeInRadians() >
			bounds[1].getLongitudeInRadians();
        
        //ArrayList<String> arguments = new ArrayList<String>();
        String[] arguments = new String[8];
        //arguments.add(Double.toString(bounds[0].getLatitudeInRadians()));
        arguments[0] = Double.toString(bounds[0].getLatitudeInRadians());
        //arguments.add(Double.toString(bounds[1].getLatitudeInRadians()));
        arguments[1] = Double.toString(bounds[1].getLatitudeInRadians());
        //arguments.add(Double.toString(bounds[0].getLongitudeInRadians()));
        arguments[2] = Double.toString(bounds[0].getLongitudeInRadians());
        //arguments.add(Double.toString(bounds[1].getLongitudeInRadians()));
        arguments[3] = Double.toString(bounds[1].getLongitudeInRadians());
        //arguments.add(Double.toString(location.getLatitudeInRadians()));
        arguments[4] = Double.toString(location.getLatitudeInRadians());
        //arguments.add(Double.toString(location.getLatitudeInRadians()));
        arguments[5] = Double.toString(location.getLatitudeInRadians());
        //arguments.add(Double.toString(location.getLongitudeInRadians()));
        arguments[6] = Double.toString(location.getLongitudeInRadians());
        //arguments.add(Double.toString(distance / GeoLocation.EARTH_RADIUS));
        arguments[7] = Double.toString(distance / GeoLocation.EARTH_RADIUS);
        
        String conc = meridian180WithinDistance?"OR":"AND";
        String sql  = "SELECT `node_id` FROM "+DBOpenHelper.TABLE_NODES+" " +
                      "WHERE `type` LIKE 'POI' AND (Latitude >= ? AND Latitude <= ?) AND (Longitude >= ? "+conc+" Longitude <= ?) " +
                      "GROUP BY `node_id` " +
                      "HAVING " +
                      "acos(sin(?) * sin(Latitude) + cos(?) * cos(Latitude) * cos(Longitude - ?)) <= ?";//*/
        
        //Log.i(LOG_TAG, "inserting "+arguments.size()+" arguments");
        //String[] args = (String[]) arguments.toArray();
        
        String[] arguments = new String[4];

        double latRadian = Math.toRadians(latitude);

        double degLatKm = 110.574235;
        double degLongKm = 110.572833 * Math.cos(latRadian);
        double deltaLat = distance / 1000.0 / degLatKm;
        double deltaLong = distance / 1000.0 / degLongKm;

        double minLat = latitude - deltaLat;
        double minLong = longitude - deltaLong;
        double maxLat = latitude + deltaLat;
        double maxLong = longitude + deltaLong;

        arguments[0] = Double.toString(minLat);
        arguments[1] = Double.toString(maxLat);
        arguments[2] = Double.toString(minLong);
        arguments[3] = Double.toString(maxLong);

        String sql = "SELECT `node_id` FROM "+DBOpenHelper.TABLE_NODES+" " +
                     "WHERE `type` LIKE 'POI' AND latitude > ? AND latitude < ? " +
                     "AND longitude > ? AND longitude < ? ";
        
        Cursor nCursor = mDb.rawQuery(sql, arguments);
        //List<INode> nodes = new ArrayList<INode>();
        if(nCursor != null && nCursor.getCount() > 0)
        {
            nCursor.moveToFirst();
            int id;
            int id_index = nCursor.getColumnIndex("node_id");
            while(!nCursor.isAfterLast())
            {
                id = nCursor.getInt(id_index);
                try {
                    n = getNodeById(id);
                    n.addCategory(Categories.POI);
                    nodes.add(n);
                } catch (NoItemFoundException ex) {
                    Log.w(LOG_TAG, "node with id "+id+" not found: "+ex.getMessage());
                }
                nCursor.moveToNext();
            }
        }//*/
        return nodes;
    }

    public List<Node> findNodesNear(Node node, int distance) throws NoItemFoundException {
        return findNodesNear(node.getLatitude(),node.getLongitude(),distance);
    }

    public Node getNodeByName(String name) throws NoItemFoundException {
        Cursor c = mDb.query(DBOpenHelper.TABLE_NODES, null, "`name` LIKE "+name, null, null, null, null);
        if(c == null || c.getCount() < 1)
            throw new NoItemFoundException("INode", "name: "+name);
        
        c.moveToFirst();
        
        Node n = parseNode(c);
        /*long id = c.getLong(c.getColumnIndexOrThrow("node_id"));
        NodeType type = NodeType.valueOf(c.getString(c.getColumnIndexOrThrow("type")));
        Date date = new Date(c.getLong(c.getColumnIndexOrThrow("date")));
        double longitude = c.getDouble(c.getColumnIndexOrThrow("longitude"));
        double latitude = c.getDouble(c.getColumnIndexOrThrow("latitude"));
        double altitude = c.getDouble(c.getColumnIndexOrThrow("altitude"));
        float accuracy = c.getFloat(c.getColumnIndexOrThrow("accuracy"));
        String address = c.getString(c.getColumnIndexOrThrow("address"));
        boolean generated = (c.getInt(c.getColumnIndexOrThrow("generated")) > 0);*/
        
        c.close();
        
        return n;
        //return new IdNode(id, name, type, date, latitude, longitude, altitude, accuracy, address, generated);
    }
    
    public Node getNodeById(long id) throws NoItemFoundException {
        Log.i(LOG_TAG, "Querying for node with id "+id);
        Cursor c = mDb.query(DBOpenHelper.TABLE_NODES, null, "`node_id` = "+id, null, null, null, null);
        if(c == null || c.getCount() < 1)
            throw new NoItemFoundException("INode","id:"+id);
        
        c.moveToFirst();
        
        Node n = parseNode(c);
        /*String name = c.getString(c.getColumnIndexOrThrow("name"));
        
        String cType = c.getString(c.getColumnIndexOrThrow("type"));
        Log.i(LOG_TAG, "Trying to cast node of type "+cType);
        NodeType type = NodeType.valueOf(cType);
        Date date = new Date(c.getLong(c.getColumnIndexOrThrow("date")));
        double longitude = c.getDouble(c.getColumnIndexOrThrow("longitude"));
        double latitude = c.getDouble(c.getColumnIndexOrThrow("latitude"));
        double altitude = c.getDouble(c.getColumnIndexOrThrow("altitude"));
        float accuracy = c.getFloat(c.getColumnIndexOrThrow("accuracy"));
        String address = c.getString(c.getColumnIndexOrThrow("address"));
        boolean generated = (c.getInt(c.getColumnIndexOrThrow("generated")) > 0);*/
        
        c.close();
        
        //return new IdNode(id, name, type, date, latitude, longitude, altitude, accuracy, address, generated);$
        return n;
    }

    public List<Node> getNodesByRoute(Route r) {
        String name = r.getName();
        String sql = "SELECT t2.`node_id`, t2.`order` FROM "+DBOpenHelper.TABLE_NODES+" t1 " +
                     "LEFT JOIN "+DBOpenHelper.TABLE_ROUTES_NODES+" t2 ON t1.`node_id` = t2.`node_id` " + 
                     "LEFT JOIN "+DBOpenHelper.TABLE_ROUTES+" t3 ON t3.`route_id` = t2.`route_id` " + 
                     "WHERE t3.`name` LIKE '" + name + "' " +
                     "ORDER BY t2.`order` ASC";
        Cursor nCursor = mDb.rawQuery(sql,null);
        
        LinkedList<Node> nodes = new LinkedList<Node>();
        
        if(nCursor != null)
        {
            nCursor.moveToFirst();
            int id;
            Node n;
            while(!nCursor.isAfterLast())
            {
                id = nCursor.getInt(nCursor.getColumnIndex("node_id"));
                try {
                    n = getNodeById(id);
                    nodes.add(n);
                } catch (NoItemFoundException ex) {
                    
                }
                nCursor.moveToNext();
            }
        }
        return nodes;
    }
    
    public Node getNodeByLocation(Location loc) throws NoItemFoundException {
        return getNodeByLocation(loc.getLatitude(), loc.getLongitude(), loc.getAltitude());
    }
    
    public Node getNodeByLocation(double latitude, double longitude, double altitude) throws NoItemFoundException {
        return resolveNode(latitude, longitude, altitude);
    }

    private DbNode resolveNode(Node node) throws NoItemFoundException {
        return resolveNode(node.getLatitude(), node.getLongitude(), node.getAltitude());
    }
    
    private DbNode resolveNode(double latitude, double longitude, double altitude) throws NoItemFoundException {
        String lat = Double.toString(latitude);
        String lng = Double.toString(longitude);
        String alt = Double.toString(altitude);
        Cursor nCursor = mDb.query(DBOpenHelper.TABLE_NODES, null, "`longitude` = ? AND `latitude` = ? AND `altitude` = ?", new String[] {lat, lng, alt}, null, null, null);
        
        DbNode n = null;
        
        if(nCursor != null && nCursor.getCount() > 0){
            try {
                n = parseNode(nCursor);
                return n;
            } catch (NoItemFoundException ex) {
                Log.w(LOG_TAG, "Error parsing node on cursor[0]");
            }
            nCursor.close();
        }
        throw new NoItemFoundException("Node","{resolvedNode}");
    }

    // Parcelable    
    public int describeContents() {
        return 0;
    }

    public void writeToParcel(Parcel arg0, int arg1) {
        
    }
    
    // Custom Methods
    private DbRoute parseRoute(Cursor routeCursor) {
        try
        {
            //routeCursor.moveToFirst();
            
            int idIndex = routeCursor.getColumnIndexOrThrow("route_id");
            int nameIndex = routeCursor.getColumnIndexOrThrow(COLUMN_NAME);
            int dateIndex = routeCursor.getColumnIndexOrThrow(COLUMN_DATE);
            //int groupIndex = routeCursor.getColumnIndexOrThrow(COLUMN_GROUP);
            
            long id = routeCursor.getInt(idIndex);
            String name = routeCursor.getString(nameIndex);
            //boolean group = routeCursor.getInt(groupIndex) == 1;
            String sDate = routeCursor.getString(dateIndex);
            
            Timestamp ts = Timestamp.valueOf(sDate);
            Date date = new Date(ts.getTime());
            date.setMinutes(date.getMinutes()-ts.getTimezoneOffset());
            
            Log.i(LOG_TAG, "received id: "+id+", name: "+name+", date: "+sDate);
            
            return new DbRoute(id, name, date);
        } catch(IllegalArgumentException ex) {
            Log.w(LOG_TAG, "Error while parsing route: "+ex.getMessage());
            return null;
        }
    }
    
    private DbNode parseNode(Cursor nodeCursor) throws NoItemFoundException {
        try {
            //nodeCursor.moveToFirst();
            
            long id = nodeCursor.getLong(nodeCursor.getColumnIndexOrThrow("node_id"));
            String name = nodeCursor.getString(nodeCursor.getColumnIndexOrThrow("name"));
            NodeType type = NodeType.valueOf(nodeCursor.getString(nodeCursor.getColumnIndexOrThrow("type")));
            Date date = new Date(nodeCursor.getLong(nodeCursor.getColumnIndexOrThrow("date")));
            double longitude = nodeCursor.getDouble(nodeCursor.getColumnIndexOrThrow("longitude"));
            double latitude = nodeCursor.getDouble(nodeCursor.getColumnIndexOrThrow("latitude"));
            double altitude = nodeCursor.getDouble(nodeCursor.getColumnIndexOrThrow("altitude"));
            float accuracy = nodeCursor.getFloat(nodeCursor.getColumnIndexOrThrow("accuracy"));
            String address = nodeCursor.getString(nodeCursor.getColumnIndexOrThrow("address"));
            boolean generated = (nodeCursor.getInt(nodeCursor.getColumnIndexOrThrow("generated")) > 0);
            
            return new DbNode(id, name, type, date, latitude, longitude, altitude, accuracy, address, generated);
            
        } catch(Exception ex) {
            Log.w(LOG_TAG, "Error while parsing node: "+ex.getMessage());
            throw new NoItemFoundException("DbNode","{parsedNode}");
        }
    }

    public List<Node> getPois() throws NoItemFoundException {
        ArrayList<Node> nodes = new ArrayList<Node>();
        
        Cursor c = mDb.query(DBOpenHelper.TABLE_NODES, null, "`type` LIKE 'poi'", null, null, null, null);
        if(c == null || c.getCount() < 1)
            throw new NoItemFoundException("INode","type: poi");
        
        c.moveToFirst();
        
        Node n;
        while(!c.isAfterLast()) {
            n = parseNode(c);
            nodes.add(n);
            c.moveToNext();
        }
        c.close();
        
        return nodes;
    }

}